package src.view;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.EnumMap;
import java.util.HashMap;

import javax.imageio.ImageIO;

import src.view.ViewHandler.DisplayType;
import src.view.ViewHandler.ImageType;

/**
 * <b>Overview</b>
 * A Viewport handles what the user sees about a certain aspect of the game, such as the map or status.  Each Viewport handles a specific aspect of the display and is confined to a region on the display.
 *
 * <b>Responsibilities</b>
 * Displays a portion of what the user will see within its scope.
 *
 * <b>Collaborators</b>
 * Drawable - Client: the Drawable object that represents the canvas for this Viewport must draw the Viewport's graphical representation.
 *
 * <b>Implementor</b> - Bruce
 * <b>Tester</b> - Tim
 *
 * @author Bruce
 */
public abstract class Viewport
{
	private static final int TOP = 28;

	/**
	 * The buffered image to display to the screen.
	 */
	private BufferedImage buffer;

	/**
	 * The graphics context to draw to the buffered image.
	 */
	private Graphics2D graphics;

	/**
	 * The area within which to draw to.
	 */
	private Dimension dimension;

	/**
	 * The topleft-most point in the drawing area.
	 */
	private Point2D topLeft = new Point2D(0, 0);

	/**
	 * The Drawable object on which to display this Viewport.
	 */
	private Drawable canvas;

	/**
	 * The display type this viewport appears under.
	 */
	private DisplayType type;

	private ViewHandler vh;

	/**
	 * Creates a new Viewport.
	 *
	 * @param dimension the dimensions of this Viewport.
	 * @param topLeft the point on the display representing the top-left corner of the Viewport.
	 * @param canvas the Drawable object on which to display this Viewport.
	 */
	public Viewport(ViewHandler vh, Drawable canvas, DisplayType type)
	{
		this.canvas = canvas;
		this.type = type;
		this.vh = vh;
		resize();
	}

	protected BufferedImage getImage(ImageType type)
	{
		return vh.getImage(type);
	}

	/**
	 * @return the topleft-most point in the drawing area.
	 */
	public final Point2D getTopLeft()
	{
		return topLeft;
	}

	/**
	 * @return the height of the drawing area, in pixels.
	 */
	public final int getHeight()
	{
		return dimension.height;
	}

	/**
	 * @return the width of the drawing area, in pixels.
	 */
	public final int getWidth()
	{
		return dimension.width;
	}

	/**
	 * Refreshes the display.
	 */
	public final void refresh(DisplayType type)
	{
		if(this.type == type)
		{
//			long time = System.currentTimeMillis();
			draw();
			canvas.draw(buffer, getTopLeft().getX(), getTopLeft().getY(), getWidth(), getHeight());
//			System.out.println(toString() + ": " + (System.currentTimeMillis() - time));
		}
	}

	/**
	 * @return the graphics context for the buffered image of this Viewport.
	 */
	protected final Graphics2D getCanvas()
	{
		return graphics;
	}

	/**
	 * Resizes this viewport to match the canvas.
	 */
	public final void resize()
	{
		this.dimension = getResizeDimension();
		this.topLeft = getResizePoint();
		BufferedImage temp = buffer;
		buffer = new BufferedImage(dimension.width, dimension.height, BufferedImage.TYPE_INT_ARGB);
		graphics = (Graphics2D)buffer.getGraphics();
		if(temp != null)
			graphics.drawImage(temp, 0, 0, null);
	}

	/**
	 * @return the dimensions of the drawable target object.
	 */
	protected final Dimension getCanvasDimensions()
	{
		return canvas.getSize();
	}

	protected void drawString(String string, int x, int y, Color bg, Color fg, int size)
	{
		getCanvas().setFont(new Font("Tempus Sans ITC", Font.BOLD, size));
		getCanvas().setColor(bg);
		getCanvas().drawString(string, x + 2, y + 2);
		getCanvas().setColor(fg);
		getCanvas().drawString(string, x, y);
	}

	protected void drawString(String string, int x, int y, Color bg, Color fg)
	{
		drawString(string, x, y, bg, fg, 16);
	}

	protected void drawString(String string, int x, int y)
	{
		drawString(string, x, y, Color.BLACK, Color.WHITE);
	}

	protected final void clicked(DisplayType display, Point p)
	{
		if(display == type)
		{
			Point pnt = new Point((int)p.getX(), (int)p.getY() - TOP);
			if(contains(pnt))
				click(new Point((int)(pnt.getX() - topLeft.getX()), (int)(pnt.getY() - topLeft.getY())));
		}
	}

	protected final boolean contains(Point p)
	{
		int minX = topLeft.getX();
		int maxX = minX + getWidth();
		int minY = topLeft.getY();
		int maxY = minY + getHeight();
		return p.getX() >= minX && p.getX() <= maxX && p.getY() >= minY && p.getY() <= maxY;
	}

	protected void click(Point p)
	{}

	/**
	 * Draws the component to the buffer.
	 */
	protected abstract void draw();

	protected abstract Dimension getResizeDimension();

	protected abstract Point2D getResizePoint();
}
